﻿#include "MIDIFile.h"
#include "io/ByteArrayInputStream.h"
#include "lang/Exception.h"
#include "util/Util.h"
#include "lang/String.h"
#include "io/ByteConvert.h"
using namespace yzrilyzr_io;
using namespace yzrilyzr_array;
using namespace yzrilyzr_lang;
namespace yzrilyzr_util{
	MIDIFile::MIDISequence * MIDIFile::parse(InputStream & is){
		MIDISequence * sequence=new MIDISequence();
		ByteArray magic(4);
		try{
			int32_t lengthLimit=500 * 1024 * 1024;
			Util::readFully(is, magic);
			String sMagic=magic;
			if(sMagic == "RIFF"){
				Util::readFully(is, magic);
				Util::readFully(is, magic);
				sMagic=magic;
				if(sMagic == "RMID"){
					Util::readFully(is, magic);
					sMagic=magic;
					if(sMagic != "data") throw Exception("Not data");
					Util::readFully(is, magic);
					Util::readFully(is, magic);
				} else if(sMagic == "MIDS"){
					//"fmt "
					Util::readFully(is, magic);
					//formatBlockLength
					Util::readFully(is, magic);
					uint32_t formatBlockLength=ByteConvert::getInt(magic, 0, false);
					ByteArray formatBlock(formatBlockLength);
					//formatBlock
					Util::readFully(is, formatBlock);
					//dwTimeFormat : Low word - time format in SMF format
					//cbMaxBuffer : Guaranteed max buffer size (default is 4096)
					//dwFlags: Format flags (default is 1)
					//"data"
					Util::readFully(is, magic);
					sMagic=magic;
					if(sMagic != "data") throw Exception("Not data");
					//dataLength
					Util::readFully(is, magic);
					uint32_t dataLength=ByteConvert::getInt(magic, 0, false);
					//block count
					Util::readFully(is, magic);
					uint32_t blockCount=ByteConvert::getInt(magic, 0, false);
					MIDIChannelEvent * last=nullptr;
					Track * track=new Track();
					for(u_index i=0;i < blockCount;i++){
						Util::readFully(is, magic);
						uint32_t tkStart=ByteConvert::getInt(magic, 0, false);
						Util::readFully(is, magic);
						uint32_t cbBuffer=ByteConvert::getInt(magic, 0, false);
						if(cbBuffer == 0)cbBuffer=2728;
						ByteArray block(cbBuffer);
						Util::readFully(is, block);
						ByteArrayInputStream bais(block);
						while(bais.available() > 0){
							Util::readFully(bais, magic);
							uint32_t iTicks=ByteConvert::getInt(magic, 0, false);
							Util::readFully(bais, magic);
							uint32_t iStreamID=ByteConvert::getInt(magic, 0, false);
							Util::readFully(bais, magic);
							uint32_t iEvent=ByteConvert::getInt(magic, 0, false);
							uint32_t deltaTimeTicks=iTicks;
							uint8_t iEventType=(iEvent >> 24) & 0xff;
							if(iEventType == 0x00){
								uint8_t type=(iEvent >> 0) & 0xff;
								uint8_t v1=(iEvent >> 8) & 0xff;
								uint8_t v2=(iEvent >> 16) & 0xff;
								if(type <= 0x7f){
									/*if(last == nullptr) throw Exception("Corrupted MIDI File");
									last=last->newInstance(deltaTimeTicks, type, *ids2);
									track->events.add(last);
									continue;*/
									throw Exception("Corrupted MIDS File");
								}
								//MIDI short message
								switch(type & 0b11110000){
									case 0x80:
									case 0x90:
									case 0xa0:
										last=new MIDINote(deltaTimeTicks, type, v1, v2);
										track->events.add(last);
										break;
									case 0xb0:
										last=new MIDIChannelControl(deltaTimeTicks, type, v1, v2);
										track->events.add(last);
										break;
									case 0xc0:
										last=new MIDIProgramChange(deltaTimeTicks, type, v1);
										track->events.add(last);
										break;
									case 0xd0:
										last=new MIDIChannelPressure(deltaTimeTicks, type, v1);
										track->events.add(last);
										break;
									case 0xe0:
										last=new MIDIPitchBend(deltaTimeTicks, type, v1, v2);
										track->events.add(last);
										break;
									case 0xf0:
										throw Exception("Corrupted MIDS File");
								}
							} else if(iEventType == 0x01){
								//tempo change
								uint32_t tempo=iEvent & 0xffffff;
								FFMessage * message=new FFMessage(deltaTimeTicks);
								message->type=0x51;
								message->quarterNoteDurationMicroSeconds=tempo;
								track->events.add(last);
							}
						}
					}
					track->events.trimToSize();
					sequence->tracks.add(track);
					sequence->tracks.trimToSize();
					return sequence;
				} else throw Exception("RIFF not support");
			}
			sMagic=magic;
			if(sMagic != "MThd") throw Exception("Not MThd");
			//head
			u_index headLength=0;
			ByteArrayInputStream bais(Util::readByteArray(is, lengthLimit, &headLength));
			DataInputStream ids(bais);
			/**
			 * 0 : mid 文件只有一条轨道 , 所有的通道都在一条轨道中 ;
			 * 1 : mid 文件有多个音轨 , 并且是同步的 , 即所有的轨道同时播放 ;
			 * 2 : mid 文件有多个音轨 , 不同步 ;
			 */
			sequence->midiFormat=ids.readShort();
			sequence->midiTrackCount=ids.readShort();
			short basicTime=ids.readShort();
			bool flg=((basicTime >> 15) & 0b1) == 0;
			if(flg){
				sequence->ticksForQuarterNote=basicTime;
			} else{
				sequence->framePerSecond=(basicTime >> 8) & 0b1111111;
				sequence->ticksPerFrame=basicTime & 0b11111111;
			}
			for(int32_t i=0;i < sequence->midiTrackCount;i++){
				Util::readFully(is, magic, 0, 4);
				sMagic=magic;
				if(sMagic != "MTrk") throw Exception("Not MTrk");
				Track * track=new Track();
				u_index midiTrackLength=0;
				ByteArray midiTrack=Util::readByteArray(is, lengthLimit, &midiTrackLength);
				parseTrack(track, midiTrack);
				sequence->tracks.add(track);
			}
			sequence->tracks.trimToSize();
			return sequence;
		} catch(Exception & e){
			std::cerr << e.what() << std::endl;
			delete sequence;
			return nullptr;
		}
	}
	void MIDIFile::parseTrack(Track * track,const  ByteArray& midiTrack){
		ByteArrayInputStream bais1(midiTrack);
		DataInputStream ids2(bais1);
		MIDIChannelEvent * last=nullptr;
		while(ids2.available() > 0){
			int32_t deltaTimeTicks=Util::readVarintFromStream(ids2);
			int32_t type=ids2.read();
			if(type <= 0x7f){
				if(last == nullptr) throw Exception("Corrupted MIDI File");
				last=last->newInstance(deltaTimeTicks, type, ids2);
				track->events.add(last);
				continue;
			}
			int32_t v1, v2;
			switch(type & 0b11110000){
				case 0x80:
				case 0x90:
				case 0xa0:
					v1=ids2.read();
					v2=ids2.read();
					last=new MIDINote(deltaTimeTicks, type, v1, v2);
					track->events.add(last);
					break;
				case 0xb0:
					v1=ids2.read();
					v2=ids2.read();
					last=new MIDIChannelControl(deltaTimeTicks, type, v1, v2);
					track->events.add(last);
					break;
				case 0xc0:
					last=new MIDIProgramChange(deltaTimeTicks, type, Util::readVarintFromStream(ids2));
					track->events.add(last);
					break;
				case 0xd0:
					v1=ids2.read();
					last=new MIDIChannelPressure(deltaTimeTicks, type, v1);
					track->events.add(last);
					break;
				case 0xe0:
					v1=ids2.read();
					v2=ids2.read();
					last=new MIDIPitchBend(deltaTimeTicks, type, v1, v2);
					track->events.add(last);
					break;
				case 0xf0:
					switch(type){
						case 0xF0:
						case 0xF7:
						{
							SysexMessage * sysexMessage=new SysexMessage(deltaTimeTicks);
							track->events.add(sysexMessage);
							u_index blength=Util::readVarintFromStream(ids2);
							sysexMessage->b=new ByteArray(blength);
							Util::readFully(ids2, *sysexMessage->b, 0, blength);
							break;
						}
						case 0xFF:
						{
							FFMessage * message=new FFMessage(deltaTimeTicks);
							track->events.add(message);
							message->type=ids2.read();
							u_index blength=Util::readVarintFromStream(ids2);
							if(message->type == 0x2f && blength != 0)
								throw Exception("Bad Track End");
							message->b=new ByteArray(blength);
							Util::readFully(ids2, *message->b, 0, blength);
							switch(message->type){
								case 0x00://序列号
									break;
								case 0x01://文本
								case 0x02://版权
								case 0x04://乐器类型
								case 0x05://歌词
								case 0x06://某点名称，例：第一乐章
								case 0x07://cue point
									//message->text = (message->b);
									break;
								case 0x03://轨道名称
									//message->text = (message->b);
									track->name=message->text;
									break;
								case 0x20://通道前缀
									break;
								case 0x51://四分音符时长
									if(message->b->length != 3)
										throw Exception("length != 3");
									message->quarterNoteDurationMicroSeconds=0;
									message->quarterNoteDurationMicroSeconds|=((*message->b)[0] & 0xff) << 16;
									message->quarterNoteDurationMicroSeconds|=((*message->b)[1] & 0xff) << 8;
									message->quarterNoteDurationMicroSeconds|=((*message->b)[2] & 0xff);
									break;
								case 0x54://SMPTE offset
									break;
								case 0x58://拍号
									if(message->b->length != 4)
										throw Exception("length != 4");
									message->beatNumerator=(*message->b)[0];
									message->beatDenominator=(int)pow(2, (*message->b)[1]);
									message->ticksPerMidiClk=(*message->b)[2];
									message->ticks24MidiClk=(*message->b)[3];
									break;
								case 0x59://调号
									break;
							}
							break;
						}
						default:
							throw Exception(type);
					}
					break;
				default:
					throw Exception(type);
			}
		}
		track->events.trimToSize();
	}
}